﻿using System;
using System.Collections.Generic;
using System.Messaging;
using System.Threading;

namespace TripRemoteObject
{
    public class MessageQueueMgr
    {
        #region Declarations

        public Dictionary<long, ResponseClientEvent> OperationList = new Dictionary<long, ResponseClientEvent>();
        readonly object _operationListData = new object();
        private readonly string QueuePaymentClientReceive = Properties.Settings.Default.QueuePaymentClientReceive;
        public long IdOperacion;

        #endregion

        #region Singleton
        static readonly MessageQueueMgr instance = new MessageQueueMgr();

        static MessageQueueMgr()
        {
        }

        MessageQueueMgr()
        {
        }

        public static MessageQueueMgr Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region OperationList Management

        private void DeleteResponseEvent(long operationId)
        {
            lock (_operationListData)
            {
                OperationList.Remove(operationId);
            }
        }

        private ResponseClientEvent GetResponseEvent(long operationId)
        {
            lock (_operationListData)
            {
                return OperationList[operationId];
            }
        }

        public long AddOperation(int userId, AutoResetEvent resetEvent)
        {
            lock (_operationListData)
            {
                ResponseClientEvent responseClientEvent = new ResponseClientEvent
                {
                    EventData = resetEvent,
                    UserId = userId
                };
                IdOperacion++;
                OperationList.Add(IdOperacion, responseClientEvent);
                return IdOperacion;
            }
        }

        #endregion

        public void StartReceive()
        {
            MessageQueue msMqReceived = new MessageQueue(QueuePaymentClientReceive);
            try
            {
                msMqReceived.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
                msMqReceived.ReceiveCompleted += MyReceiveCompleted;
                msMqReceived.Purge();
                msMqReceived.BeginReceive();
            }
            catch (MessageQueueException ee)
            {
                Console.Write(ee.ToString());
                msMqReceived.Close();
            }
            catch (Exception eee)
            {
                Console.Write(eee.ToString());
                msMqReceived.Close();
            }

        }

        private void MyReceiveCompleted(object source, ReceiveCompletedEventArgs asyncResult)
        {
            MessageQueue msMqReceived = (MessageQueue)source;
            try
            {
                msMqReceived.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
                Message message = msMqReceived.EndReceive(asyncResult.AsyncResult);

                //================== realizar operacion sobre el mensaje =============================
                if (message != null && message.Body != null)
                {
                    var result = message.Body.ToString().Split('|');
                    if (result.Length == 4)
                    {
                        int operationId = Convert.ToInt32(result[0]);
                        int userId = Convert.ToInt32(result[1]);
                        decimal amount = Convert.ToDecimal(result[2]);
                        bool isPrepaid = Convert.ToBoolean(result[3]);

                        ResponseClientEvent responseClient = GetResponseEvent(operationId);

                        responseClient.AmountState = GetState(amount, isPrepaid);
                        responseClient.EventData.Set();
                    }
                }
                //====================================================================================
            }
            catch (MessageQueueException ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                msMqReceived.BeginReceive();
            }
        }

        private AmountState GetState(decimal amount,bool isPrepaid)
        {
            if (!isPrepaid)
                return AmountState.Ok;
            if (amount == 0)
                return AmountState.NoCredit;
            if (amount < Properties.Settings.Default.AlertAmount)
                return AmountState.LowBalance;
            return AmountState.Ok;
        }

        public AmountState GetAmountState(long operationId, AutoResetEvent resetEvent)
        {
            var res = resetEvent.WaitOne(Properties.Settings.Default.MaxWaitForQueue);
            if (res)
            {
                ResponseClientEvent respEvent = GetResponseEvent(operationId);
                DeleteResponseEvent(operationId);
                return respEvent.AmountState;
            }
            DeleteResponseEvent(operationId);
            throw new Exception("Operation time out");
        }

    }
}
